use std::{collections::HashMap, rc::Rc};

use super::Attribute;

pub struct Material {
    pipeline: Rc<wgpu::RenderPipeline>,
    attrs: HashMap<u32, Box<dyn Attribute>>,
}

impl Material {
    pub fn new(pipeline: Rc<wgpu::RenderPipeline>) -> Self {
        Self {
            pipeline,
            attrs: HashMap::new(),
        }
    }

    pub fn pipeline(&self) -> Rc<wgpu::RenderPipeline> {
        Rc::clone(&self.pipeline)
    }

    pub fn put(&mut self, bind_point: u32, attr: Box<dyn Attribute>) {
        self.attrs.insert(bind_point, attr);
    }

    pub fn attr<A: Attribute>(&self, bind_point: u32) -> Option<&A> {
        self.attrs.get(&bind_point).and_then(|a| a.downcast())
    }

    pub fn attr_mut<A: Attribute>(&mut self, bind_point: u32) -> Option<&mut A> {
        self.attrs
            .get_mut(&bind_point)
            .and_then(|a| a.downcast_mut())
    }

    pub fn prepare(&mut self, ctx: &super::Context) {
        for attr in self.attrs.values_mut() {
            attr.prepare(ctx)
        }
    }

    pub fn set<'p>(&'p self, pass: &mut wgpu::RenderPass<'p>) {
        pass.set_pipeline(&self.pipeline);
        for (point, attr) in self.attrs.iter() {
            pass.set_bind_group(*point, attr.bind_group(), &[]);
        }
    }
}
